include([
         'renderers.All'
         , 'com.unknown.GameWindow'
         , 'HexGrid'], 
function(){

	declare('PathBuilder');
	
	var FILL = Direction._LAST*2;
	
	var sideMap = [];
	sideMap[Direction.UP | Direction.LEFT] = 1;
	sideMap[Direction.UP] = 2;
	sideMap[Direction.UP | Direction.RIGHT] = 4;
	sideMap[Direction.DOWN | Direction.RIGHT] = 8;
	sideMap[Direction.DOWN] = 16;
	sideMap[Direction.DOWN | Direction.LEFT] = 32;
	
	var ALL = 64-1;
//16,32 8,16
	var HEX_PATH_PREFIX = [
		{id :'v-l', sides:[[1,32],[2,4,8,16]], outer:'v-l', inner:'v-r2', curve:null}
		,{id:'v-r', sides:[[4,8],[1,2,16,32]], outer:'v-r', inner:'v-l2', curve:null}
		,{id:'d3-rb', sides:[[8,16],[1,2,4,32]], outer:'d3-rb', inner:'d4-lb', curve:null}
		,{id:'d3-rt', sides:[[1,2],[4,8,16,32]], outer:'d3-rt', inner:'d4-lt', curve:null}
		,{id:'d3-lt', sides:[[2,4],[1,8,16,32]], outer:'d3-lt', inner:'d4-rt', curve:null}
		,{id:'d3-lb', sides:[[16,32],[1,2,4,8]], outer:'d3-lb', inner:'d4-rb', curve:null}
		,{id:'d1-rt', sides:[[1,2,32]], curve:'in'}
		,{id:'d1-lb', sides:[[4,8,16]], curve:'in'}
		,{id:'d2-lb', sides:[[1,16,32]], curve:'in'}
		,{id:'d2-rt', sides:[[2,4,8]], curve:'out'}
		,{id:'h-lt' , sides:[[1,2,4]], curve:'out'}
		,{id:'h-ub' , sides:[[8,16,32]], curve:'in'}
		,{id:'d1-lt', sides:[[1]], curve:'in'}
		,{id:'d1-rb', sides:[[8]], curve:'out'}
		,{id:'d2-lt', sides:[[4]], curve:'in'}
		,{id:'d2-rb', sides:[[32]], curve:'out'}
		,{id:'h-lb' , sides:[[16]], curve:'out'}
		,{id:'h-ut' , sides:[[2]], curve:'in'}
	];
	var HEX_ENG_PATH_PREFIX = [
		{id :'inner.east'      , sides:[[4,8],[1,2,16,32]]}
		,{id:'outer.east'      , sides:[[1,32],[2,4,8,16]]}
		,{id:'outer.south.east', sides:[[8,16],[1,2,4,32]]}
		,{id:'inner.south.east', sides:[[1,2],[4,8,16,32]]}
		,{id:'inner.south.west', sides:[[2,4],[1,8,16,32]]}
		,{id:'outer.south.west', sides:[[16,32],[1,2,4,8]]}
		,{id:'outer.north.west', sides:[[1,2,32],[4,8,16]]}
		,{id:'inner.north.west', sides:[[8]]}
		,{id:'inner.south.east', sides:[[1]]}
		,{id:'outer.south.east', sides:[[4,8,16],[1,2,32]]}
		,{id:'inner.south.west', sides:[[4]]}
		,{id:'outer.south.west', sides:[[1,16,32],[2,4,8]]}
		,{id:'inner.north.east', sides:[[32]]}
		,{id:'outer.north.east', sides:[[2,4,8],[1,16,32]]}
		,{id:'outer.north'     , sides:[[1,2,4],[8,16,32]]}
		,{id:'inner.north'     , sides:[[16]]}
		,{id:'outer.south'     , sides:[[8,16,32],[1,2,4]]}
		,{id:'inner.south'     , sides:[[2]]}
	];
		
	PathBuilder = function(config){
		if( config ){
			this._sides  = config.sides || 6
			this._blocks = config.blocks || HEX_PATH_PREFIX
		}else{
			this._sides = 6;
			this._blocks = HEX_PATH_PREFIX;
		}
		var denormalized = []
		iterate(this._blocks, function(block){
			iterate(block.sides, function(sides){
				denormalized.push({id:block.id, sides:sides, curve:block.curve, inner:block.inner, outer:block.outer});
			})
		});
		this._blocks = denormalized.sort(function(a,b){
			return b.sides.length - a.sides.length;
		})
	}
	
	PathBuilder.prototype = {
		getOpenBorders : function(cell){
			var grid = cell.owner;
			var neighbors = grid.getNeighbors(cell);
			var hasPath = [];
			
			iterate(neighbors, function(item, index){
				if( !item.path ) return;
				
				hasPath.push(sideMap[index]);
			});
			return hasPath;
		},
		_orArray : function(array){
			var total =0;
			iterate(array, function(i){ total = total | i; })
			return total;
		},
		_checkNoDuplicates : function(array){
			var store = [];
			for(var i=0; i < array.length; i++){
				if( store[array[i]] ) return false;
				store[array[i]] = array[i];
			}
			return true;
		},
		_checkPigeonHole : function(array, array2){
			var array3 = [];
			var holesAvailable = 0;
			array.forEach(function(i){ array3.push(i)})
			array2.forEach(function(i){ array3.push(i)})
			holesAvailable = array3.length <= this._sides;
			return holesAvailable && this._checkNoDuplicates(array3);
		},
		_solve : function(result, openBorders, sum){
			var scope = this;
			iterate(this._blocks, function(guess){
				var valid = scope._checkPigeonHole(openBorders, guess.sides);
				if( valid ){
					var s2 = (sum | scope._orArray(guess.sides));
					var r2 = result.concat([]);
					r2.push(guess);
					var o2 = openBorders.concat(guess.sides);
					
					var solution = scope._solve(r2, o2, s2);
					var count = 0;
					solution.forEach(function(i){ count += (i.sides?i.sides.length:1)})
					if( count == 6 ){
						result = solution;
						return result;
					}
				}
			});
			return result;
		},		
		buildPath : function(cell){
			
			var openBorders = this.getOpenBorders(cell);
			var orResult = this._orArray(openBorders);
			if( orResult == ALL ) return [];
			
			var result = openBorders.concat([]);
			
			result = this._solve(result, openBorders, orResult);

            var tmp = [];
            iterate(result, function(b){
                if( b.id ) tmp.push(b);
            });
			return tmp;
		},
		matchOpenBorders : function(cell, chunks){
			var openBorders = this._orArray(this.getOpenBorders(cell));
			//console.log(openBorders);
			var scope = this;
			var result = [];
			iterate(chunks, function(chunk){
				var total = scope._orArray(chunk.sides);
				if( (total & openBorders) == total ){
					result.push( chunk );
				}
			});
			//console.log(result);
			return result;
		}
	}
	PathBuilder.HEX_FILL = [
	{id:'d1-rf', sides:[4,16]}
	,{id:'d1-lf', sides:[2,32]}
	,{id:'d2-rf', sides:[2,8]}
	,{id:'d2-lf', sides:[1,16]}
	,{id:'d3-lf', sides:[1,8]}
	,{id:'d3-rf', sides:[32,4]}
	,{id:'h-lf', sides:[32,8]}
	,{id:'h-uf', sides:[1,4]}
	,{id:'v-f', sides:[2,16]}
	];
})